﻿
using Boilen.Primitives.Members;
using System;
using System.Collections.Generic;
using System.ComponentModel;


namespace Boilen.Primitives.Implementers {

    /// <summary>
    /// Implements the <see cref="INotifyPropertyChanged"/> interface.
    /// </summary>
    public sealed class NotifyPropertyChangedInterface : InterfaceImplementer<INotifyPropertyChanged> {

        /// <summary>
        /// Gets or sets a value indicating whether to use the implementation defined on the base class.
        /// </summary>
        public bool InheritImplementation { get; set; }


        /// <inheritdoc/>
        protected override IEnumerable<Member> InterfaceMembers {
            get {
                // "PropertyChanged" event.
                Type baseType = this.Parent.Type.BaseType;
                bool baseImplementsInterface = typeof( INotifyPropertyChanged ).IsAssignableFrom( baseType );
                if( !baseImplementsInterface && !this.InheritImplementation ) {
                    var existingEvent = typeof( INotifyPropertyChanged ).GetEvent( "PropertyChanged" );
                    var propertyChangedEvent = new Event<PropertyChangedEventHandler>( this.Parent, existingEvent );
                    propertyChangedEvent.Prepare( );
                    foreach( var eventMember in propertyChangedEvent.Members )
                        yield return eventMember;
                }
            }
        }


        /// <inheritdoc/>
        public NotifyPropertyChangedInterface( PartialType parent )
            : base( parent ) { }


        /// <inheritdoc/>
        public override void Prepare( ) {
            base.Prepare( );

            // Add PropertyNameConstants implementation, if one is not already defined.
            this.RequireImplementer<PropertyNameConstants>( this.Condition, c => this.Parent.ImplementPropertyNameConstants( c ) );

            this.PrepareTargets( ( ITarget t ) => t.Prepare( this ) );
        }

        /// <summary>
        /// Assigns the values used for the <see cref="InheritImplementation"/> property.
        /// </summary>
        public NotifyPropertyChangedInterface SetInheritImplementation( bool inheritImplementation ) {
            this.InheritImplementation = inheritImplementation;
            return this;
        }


        /// <summary>
        /// Represents an <see cref="IImplementer"/> that supports the <see cref="INotifyPropertyChanged"/> interface.
        /// </summary>
        public interface ITarget : IImplementer {
            /// <summary>
            /// Gets a value indicating whether the property should cause a change notification.
            /// </summary>
            bool ShouldNotify { get; }

            /// <summary>
            /// Called by <see cref="NotifyPropertyChangedInterface"/> to prepare a target implementer for the interface.
            /// </summary>
            void Prepare( NotifyPropertyChangedInterface implementer );
        }

    }

}
